<script setup>
import { ref, h, createRenderer, onMounted, onUnmounted } from 'vue';
import CodeBlock from '@components/CodeBlock.vue';

// 自定义渲染器的目标容器
const canvasContainer = ref(null);
let canvas = null;
let ctx = null;
let lastTime = 0;

// 自定义渲染器配置
const customRendererOptions = {
  createElement(type) {
    console.log(`创建元素: ${type}`);
    return { type };
  },
  
  createTextNode(text) {
    console.log(`创建文本: ${text}`);
    return { type: 'text', text };
  },
  
  patchProp(el, key, prevValue, nextValue) {
    console.log(`更新属性: ${key}`, prevValue, nextValue);
    if (el.props === undefined) {
      el.props = {};
    }
    el.props[key] = nextValue;
  },
  
  insert(el, parent, anchor) {
    console.log(`插入元素: ${el.type}`, parent, anchor);
    if (parent.children === undefined) {
      parent.children = [];
    }
    parent.children.push(el);
    
    // 触发重绘
    scheduleRender();
  },
  
  remove(el) {
    console.log(`移除元素: ${el.type}`);
    // 触发重绘
    scheduleRender();
  },
  
  setElementText(el, text) {
    console.log(`设置元素文本: ${text}`);
    el.text = text;
  },
  
  setText(el, text) {
    console.log(`设置文本: ${text}`);
    el.text = text;
  },
  
  nextSibling(el) {
    return null;
  },
  
  parentNode(node) {
    return null;
  }
};

// 创建自定义渲染器
const renderer = createRenderer(customRendererOptions);

// 根应用节点
const rootNode = {};

// 动画状态
const animationState = ref({
  objects: [
    { x: 50, y: 100, radius: 20, color: '#667eea', dx: 2, dy: 1 },
    { x: 150, y: 150, radius: 15, color: '#764ba2', dx: 1, dy: -2 },
    { x: 250, y: 80, radius: 25, color: '#42b983', dx: -1.5, dy: 1.5 }
  ]
});

// 移动对象的方法
const moveObjects = () => {
  if (!canvas) return;
  
  animationState.value.objects.forEach(obj => {
    // 更新位置
    obj.x += obj.dx;
    obj.y += obj.dy;
    
    // 边界检查
    if (obj.x + obj.radius > canvas.width || obj.x - obj.radius < 0) {
      obj.dx = -obj.dx;
    }
    if (obj.y + obj.radius > canvas.height || obj.y - obj.radius < 0) {
      obj.dy = -obj.dy;
    }
  });
  
  // 触发重绘
  scheduleRender();
};

// 绘制函数
const renderScene = () => {
  if (!canvas || !ctx) return;
  
  // 清空画布
  ctx.fillStyle = '#f0f0f0';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  
  // 绘制对象
  animationState.value.objects.forEach(obj => {
    ctx.beginPath();
    ctx.arc(obj.x, obj.y, obj.radius, 0, Math.PI * 2);
    ctx.fillStyle = obj.color;
    ctx.fill();
    ctx.closePath();
  });
  
  // 更新FPS显示
  updateFPS();
};

// 计划渲染
let animationId = null;
const scheduleRender = () => {
  if (!animationId) {
    animationId = requestAnimationFrame(() => {
      renderScene();
      animationId = null;
    });
  }
};

// FPS计算
let frameCount = 0;
const fps = ref(0);

const updateFPS = () => {
  const now = performance.now();
  const deltaTime = now - lastTime;
  
  frameCount++;
  
  if (deltaTime > 1000) { // 每秒更新一次
    fps.value = Math.round((frameCount * 1000) / deltaTime);
    frameCount = 0;
    lastTime = now;
  }
};

// 添加新对象
const addNewObject = () => {
  if (!canvas) return;
  
  const colors = ['#667eea', '#764ba2', '#42b983', '#ff6b6b', '#feca57'];
  const newObj = {
    x: Math.random() * (canvas.width - 40) + 20,
    y: Math.random() * (canvas.height - 40) + 20,
    radius: Math.random() * 15 + 10,
    color: colors[Math.floor(Math.random() * colors.length)],
    dx: (Math.random() - 0.5) * 4,
    dy: (Math.random() - 0.5) * 4
  };
  
  animationState.value.objects.push(newObj);
};

// 移除对象
const removeObject = () => {
  if (animationState.value.objects.length > 0) {
    animationState.value.objects.pop();
  }
};

// 暂停/恢复动画
const isPaused = ref(false);
let animationInterval = null;

const toggleAnimation = () => {
  isPaused.value = !isPaused.value;
  
  if (isPaused.value) {
    clearInterval(animationInterval);
  } else {
    animationInterval = setInterval(moveObjects, 16); // 约60fps
  }
};

// 组件挂载时初始化
const initCanvas = () => {
  if (!canvasContainer.value) return;
  
  // 创建canvas元素
  canvas = document.createElement('canvas');
  canvas.width = 800;  // 设置合适的宽度
  canvas.height = 400; // 设置合适的高度
  canvas.style.border = '1px solid #ddd';
  canvas.style.maxWidth = '100%';
  canvas.style.display = 'block';
  
  canvasContainer.value.innerHTML = '';
  canvasContainer.value.appendChild(canvas);
  
  ctx = canvas.getContext('2d');
  
  // 开始动画
  lastTime = performance.now();
  if (!animationInterval) {
    animationInterval = setInterval(moveObjects, 16); // 约60fps
  }
  
  // 使用自定义渲染器渲染组件树
  const vnode = h('custom-canvas', { width: 400, height: 300 });
  renderer.render(vnode, rootNode);
};

// 组件卸载时清理
const cleanup = () => {
  clearInterval(animationInterval);
  cancelAnimationFrame(animationId);
};

// 组件生命周期钩子
onMounted(() => {
  initCanvas();
});

onUnmounted(() => {
  cleanup();
});

// 自定义渲染器配置选项
const rendererConfig = {
  'createElement': '创建虚拟元素节点',
  'createTextNode': '创建文本节点',
  'patchProp': '更新元素属性',
  'insert': '将节点插入到父节点',
  'remove': '从父节点移除子节点',
  'setElementText': '设置元素的文本内容',
  'setText': '设置文本节点的内容',
  'nextSibling': '获取下一个兄弟节点',
  'parentNode': '获取父节点'
};
</script>

<template>
  <div class="custom-renderer-demo">
    <h2 class="demo-title">🎨 Vue 3 自定义渲染器演示</h2>
    
    <p class="demo-description">
      自定义渲染器是Vue 3的高级特性，允许将Vue组件渲染到任何平台，而不仅仅是DOM。
      本示例演示了如何创建一个自定义渲染器并将内容渲染到HTML5 Canvas上。
    </p>
    
    <div class="demo-content">
      <!-- 渲染示例区域 -->
      <div class="demo-section">
        <h3>自定义渲染器动画示例</h3>
        
        <div class="canvas-container" ref="canvasContainer"></div>
        
        <div class="canvas-info">
          <p>FPS: {{ fps }}</p>
          <p>对象数量: {{ animationState.objects.length }}</p>
        </div>
        
        <div class="control-buttons">
          <button @click="toggleAnimation" class="btn">
            {{ isPaused ? '开始动画' : '暂停动画' }}
          </button>
          <button @click="addNewObject" class="btn btn-primary">添加对象</button>
          <button @click="removeObject" class="btn btn-danger">移除对象</button>
          <button @click="initCanvas" class="btn btn-secondary">重置</button>
        </div>
      </div>
      
      <!-- 自定义渲染器说明 -->
      <div class="demo-section">
        <h3>自定义渲染器配置</h3>
        <p>自定义渲染器需要实现以下核心方法：</p>
        
        <div class="renderer-methods">
          <div v-for="(description, method) in rendererConfig" :key="method" class="method-item">
            <code class="method-name">{{ method }}</code>
            <span class="method-desc">{{ description }}</span>
          </div>
        </div>
        
        <div class="code-example">
          <CodeBlock code="// 创建自定义渲染器的示例
import { createRenderer } from 'vue'

const customRenderer = createRenderer({
  createElement(type) {
    // 创建自定义平台的元素
    return { type }
  },
  
  patchProp(el, key, prevValue, nextValue) {
    // 更新元素属性
    el[key] = nextValue
  },
  
  insert(el, parent, anchor) {
    // 将元素插入到父节点
    parent.children = parent.children || []
    parent.children.push(el)
  },
  
  // 其他必要的渲染器方法...
})

// 创建应用实例
const app = customRenderer.createApp({
  render() {
    return h('custom-element', { /* props */ })
  }
})

// 挂载到自定义根节点
app.mount({})" language="javascript"></CodeBlock>
        </div>
      </div>
      
      <!-- 使用场景 -->
      <div class="demo-section">
        <h3>自定义渲染器使用场景</h3>
        
        <ul class="use-cases">
          <li>渲染到Canvas或WebGL进行游戏或可视化开发</li>
          <li>开发跨平台桌面应用（如与Electron结合）</li>
          <li>创建移动应用界面（如使用React Native渲染器）</li>
          <li>服务器端渲染（SSR）的自定义实现</li>
          <li>渲染到PDF或SVG等特殊格式</li>
          <li>在非浏览器环境中使用Vue（如Node.js或Web Workers）</li>
        </ul>
      </div>
    </div>
    
    <!-- 实现原理 -->
    <div class="implementation-section">
      <h3>实现原理</h3>
      <p>Vue 3的自定义渲染器通过分离核心渲染逻辑和平台特定实现，使得Vue的组件系统可以在任何平台上运行。
      渲染器负责将Vue的虚拟DOM树转换为特定平台的实际渲染结果。</p>
      
      <div class="implementation-steps">
        <div class="step">
          <h4>1. 核心概念</h4>
          <p>自定义渲染器基于Vue的渲染管线，将渲染过程拆分为平台无关的部分和平台特定的部分。</p>
        </div>
        
        <div class="step">
          <h4>2. 性能考量</h4>
          <p>在实现自定义渲染器时，应注意避免频繁的渲染调用，可以使用批处理和调度机制提高性能。</p>
        </div>
        
        <div class="step">
          <h4>3. 生命周期管理</h4>
          <p>确保正确处理资源的创建和清理，避免内存泄漏。</p>
        </div>
      </div>
    </div>
  </div>
</template>



<style scoped>
.custom-renderer-demo {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.demo-title {
  text-align: center;
  margin-bottom: 10px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.demo-description {
  text-align: center;
  margin-bottom: 30px;
  color: #666;
  max-width: 800px;
  margin-left: auto;
  margin-right: auto;
}

.demo-content {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin-bottom: 40px;
}

.demo-section {
  flex: 1;
  min-width: 300px;
  background: white;
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.demo-section h3 {
  color: #667eea;
  margin-bottom: 20px;
  border-bottom: 2px solid #f0f0f0;
  padding-bottom: 10px;
}

/* 画布区域 */
.canvas-container {
  margin-bottom: 20px;
  display: flex;
  justify-content: center;
  overflow: hidden;
  max-width: 100%;
  position: relative;
}

.canvas-info {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
  padding: 10px;
  background-color: #f5f5f5;
  border-radius: 6px;
  font-weight: bold;
}

.control-buttons {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.btn {
  padding: 10px 20px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  transition: background-color 0.3s ease;
}

.btn:hover {
  opacity: 0.9;
}

.btn-primary {
  background-color: #667eea;
  color: white;
}

.btn-secondary {
  background-color: #e0e0e0;
  color: #333;
}

.btn-danger {
  background-color: #ff6b6b;
  color: white;
}

/* 渲染器方法列表 */
.renderer-methods {
  margin-bottom: 20px;
}

.method-item {
  display: flex;
  align-items: center;
  padding: 10px;
  margin-bottom: 8px;
  background-color: #f5f5f5;
  border-radius: 6px;
}

.method-name {
  font-family: 'Courier New', monospace;
  background-color: #e0e0e0;
  padding: 4px 8px;
  border-radius: 4px;
  margin-right: 10px;
  min-width: 180px;
  text-align: center;
}

.method-desc {
  color: #666;
}

/* 代码示例 */
.code-example {
  background-color: #1e1e1e;
  border-radius: 8px;
  padding: 15px;
  overflow-x: auto;
}

.code-example pre {
  margin: 0;
  font-family: 'Courier New', Courier, monospace;
  font-size: 13px;
  line-height: 1.5;
}

.code-example code {
  color: #d4d4d4;
}

/* 使用场景 */
.use-cases {
  padding-left: 20px;
}

.use-cases li {
  margin-bottom: 10px;
  padding: 5px 0;
  color: #555;
}

/* 实现原理部分 */
.implementation-section {
  background: white;
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.implementation-section h3 {
  color: #667eea;
  margin-bottom: 20px;
}

.implementation-steps {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin-top: 20px;
}

.step {
  flex: 1;
  min-width: 250px;
  padding: 15px;
  background-color: #f8f9fa;
  border-radius: 8px;
  border-left: 4px solid #667eea;
}

.step h4 {
  color: #333;
  margin-bottom: 10px;
}

/* 响应式调整 */
@media (max-width: 768px) {
  .demo-content {
    flex-direction: column;
  }
  
  .control-buttons {
    flex-direction: column;
  }
  
  .btn {
    width: 100%;
  }
  
  .method-item {
    flex-direction: column;
    align-items: flex-start;
  }
  
  .method-name {
    margin-bottom: 8px;
  }
}
</style>